package cn.yoline.xylink.activity;

import android.annotation.SuppressLint;
import android.app.ActivityManager;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.res.Configuration;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.log.L;
import android.log.LogUtils;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.Log;
import android.util.TypedValue;
import android.view.MotionEvent;
import android.view.OrientationEventListener;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;

import androidx.annotation.CheckResult;
import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.annotation.StringDef;
import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.viewpager.widget.ViewPager;


import com.ainemo.module.call.data.Enums;
import com.ainemo.module.call.data.RemoteUri;
import com.ainemo.sdk.callback.RefreshTokenCallback;
import com.ainemo.sdk.model.ConfMgmtState;
import com.ainemo.sdk.model.TokenInfo;
import com.ainemo.sdk.otf.ContentType;
import com.ainemo.sdk.otf.NemoSDK;
import com.ainemo.sdk.otf.NemoSDKListener;
import com.ainemo.sdk.otf.Orientation;
import com.ainemo.sdk.otf.Roster;
import com.ainemo.sdk.otf.RosterWrapper;
import com.ainemo.sdk.otf.SimpleNemoSDkListener;
import com.ainemo.sdk.otf.VideoInfo;
import com.ainemo.sdk.otf.WhiteboardChangeListener;
import com.trello.rxlifecycle3.LifecycleProvider;
import com.trello.rxlifecycle3.LifecycleTransformer;
import com.trello.rxlifecycle3.RxLifecycle;
import com.trello.rxlifecycle3.android.ActivityEvent;
import com.trello.rxlifecycle3.android.RxLifecycleAndroid;
import com.zhihu.matisse.Matisse;

import java.io.File;
import java.io.FileOutputStream;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

import cn.yoline.xylink.GalleryVideoFragment;
import cn.yoline.xylink.R;
import cn.yoline.xylink.SpeakerVideoFragment;
import cn.yoline.xylink.VideoFragment;
import cn.yoline.xylink.VideoPagerAdapter;
import cn.yoline.xylink.ViewPagerNoSlide;
import cn.yoline.xylink.XylinkStore;
import cn.yoline.xylink.adapter.MeetingMemberAdapter;
import cn.yoline.xylink.constant.MeetingConstant;
import cn.yoline.xylink.share.ShareState;
import cn.yoline.xylink.share.SharingValues;
import cn.yoline.xylink.share.picture.CirclePageIndicator;
import cn.yoline.xylink.share.screen.ScreenPresenter;
import cn.yoline.xylink.utils.ActivityUtils;
import cn.yoline.xylink.utils.CommonTime;
import cn.yoline.xylink.utils.GalleryLayoutBuilder;
import cn.yoline.xylink.utils.LayoutMode;
import cn.yoline.xylink.utils.SpeakerLayoutBuilder;
import cn.yoline.xylink.widget.CustomAlertDialog;
import cn.yoline.xylink.view.FullScreenVideoView;
import cn.yoline.xylink.view.SpeakerVideoGroup;
import cn.yoline.xylink.view.VideoCell;
import cn.yoline.xylink.view.VideoCellLayout;
import cn.yoline.xylink.widget.MoreItemDialog;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.functions.Consumer;
import io.reactivex.schedulers.Schedulers;
import io.reactivex.subjects.BehaviorSubject;

import static android.view.View.GONE;
import static android.view.View.VISIBLE;

/**
 * Copyright 2020 JxGIS
 *
 * @file MeetingRoomActivity
 * @auth linaisheng
 * Created on 2022/7/26.
 * Description：
 */
public class MeetingRoomActivity extends BaseActivity
        implements View.OnClickListener, VideoFragment.VideoCallback {

    public static final String CALL_NUMBER = "callNumber";
    public static final String MUTE_VIDEO = "muteVideo";
    public static final String MUTE_AUDIO = "muteAudio";

    private static final int DUAL_TYPE_CONTENT = 0;
    private static final int DUAL_TYPE_PICTURE = 3;

    private String callNumber;

    private RelativeLayout meetingToolbar;
    private ImageView ivNetworkState; // 信号
    private TextView tvCallDuration; // 通话时长
    private TextView tvCallNumber; // 号码

    private ImageView ivChangeVolume;   //切换扬声器
    private ImageView ivChangeCamera;   // 切换摄像头
    private ImageView ivHangup;         //挂断

    private ImageView ivOnOffMic;        //麦克风
    private TextView tvOnOffMic;        //麦克风

    private ImageView ivOnOffCamera;    // 关闭摄像头
    private TextView tvOnOffCamera;     // 关闭摄像头

    private TextView tvRoomMember;      //会议成员

    private LinearLayout llFinishShare;   //结束共享
    private View shareScreenView;
    private LinearLayout llLockPeople; // 锁定至屏幕
    private ImageView  ivOffFullScreen;

    private LinearLayout llHostMeeting;
    private RecyclerView rvMeetingMember;


    private boolean isToolbarShowing = false; // 隐藏控制布局
    private boolean audioMode = false;
    private boolean isMuteBtnEnable = true;
    private String muteStatus = null;
    private boolean defaultCameraFront = false; // 默认摄像头位置
    private boolean defaultVolumeSpeaker = false;
    private boolean isVideoMute = false;

    private VideoInfo fullVideoInfo;

    private boolean isCallStart;
    private List<VideoInfo> mRemoteVideoInfos;
    // 存储第一页数据, 一二页复用
    private List<VideoInfo> firstPagerVideoInfo;

    private static final int sDefaultTimeout = 5000;
    private Handler handler = new Handler();

    private CompositeDisposable compositeDisposable;

    // 屏幕方向监听
    private MyOrientationEventListener orientationEventListener;
    private boolean enableScreenOrientation = false;

    private ScreenPresenter screenPresenter;
    private ShareState shareState = ShareState.NONE;

    private ViewPagerNoSlide videoPager;
    private VideoPagerAdapter videoPagerAdapter;
    private int currentPagerIndex = 0;
    private CirclePageIndicator videoPagerIndicator;
    private MyVideoPagerListener myVideoPagerListener;
    private Observable<Boolean> confMgmtStateObserver;

    private WhiteboardChangeListener whiteboardChangeListener;

    private RosterWrapper rosterWrapper;
    private VideoInfo localVideoInfo;

    private MeetingMemberAdapter memberAdapter;

    @StringDef({
            MuteStatus.HAND_UP, MuteStatus.HAND_DOWN, MuteStatus.END_SPEACH
    })
    @Retention(RetentionPolicy.SOURCE)
    public @interface MuteStatus {
        String HAND_UP = "HAND_UP";
        String HAND_DOWN = "HAND_DOWN";
        String END_SPEACH = "END_SPEACH";
    }

    @IntDef({
            VideoStatus.VIDEO_STATUS_NORMAL, VideoStatus.VIDEO_STATUS_LOW_AS_LOCAL_BW,
            VideoStatus.VIDEO_STATUS_LOW_AS_LOCAL_HARDWARE, VideoStatus.VIDEO_STATUS_LOW_AS_REMOTE,
            VideoStatus.VIDEO_STATUS_NETWORK_ERROR, VideoStatus.VIDEO_STATUS_LOCAL_WIFI_ISSUE
    })
    @Retention(RetentionPolicy.SOURCE)
    public @interface VideoStatus {
        int VIDEO_STATUS_NORMAL = 0;
        int VIDEO_STATUS_LOW_AS_LOCAL_BW = 1;
        int VIDEO_STATUS_LOW_AS_LOCAL_HARDWARE = 2;
        int VIDEO_STATUS_LOW_AS_REMOTE = 3;
        int VIDEO_STATUS_NETWORK_ERROR = 4;
        int VIDEO_STATUS_LOCAL_WIFI_ISSUE = 5;
    }

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        final Window win = getWindow();
        win.addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
        win.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
        setContentView(R.layout.activity_meeting_room);
        compositeDisposable = new CompositeDisposable();
        initView();
        initData();
    }

    @Override
    protected void onStart() {
        super.onStart();
        defaultCameraFront = NemoSDK.defaultCameraId() == 1;
        defaultVolumeSpeaker = NemoSDK.getInstance().isSpeakerOnModeDefault();

        ivChangeVolume.setImageResource(defaultVolumeSpeaker ?
                R.drawable.icon_volume_middle : R.drawable.icon_volume_modle);

        NemoSDK.getInstance().releaseCamera();
        NemoSDK.getInstance().requestCamera();
    }

    @Override
    protected void onResume() {
        super.onResume();
        if (screenPresenter != null) {
            screenPresenter.hideFloatView();
        }
    }

    @Override
    public void onPause() {
        super.onPause();
    }

    @Override
    public void onStop() {
        super.onStop();
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
                && screenPresenter != null && screenPresenter.isSharingScreen()) {
            screenPresenter.onStop();
        }
        // 应用退到后台
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            if (!ActivityUtils.isAppForeground(this)) {
                showToast("视频通话退到后台");
            }
        }
    }

    @Override
    public void onBackPressed() {
        // Intercept back event
        hideHostMeeting();
    }

    // remember to release resource when destroy
    @Override
    public void onDestroy() {
        XylinkStore.getInstant().setIsOnMeeting(false);
        if (compositeDisposable != null && !compositeDisposable.isDisposed()) {
            compositeDisposable.dispose();
        }
        if (screenPresenter != null) {
            screenPresenter.onDestroy();
        }
        orientationEventListener.disable();

        List<VideoFragment> fragments = videoPagerAdapter.getFragments();
        for (VideoFragment videoFragment : fragments) {
            videoFragment.onDestroy();
        }
        super.onDestroy();
    }

    private void initView() {
        meetingToolbar = findViewById(R.id.meeting_toolbar);
        ivNetworkState = findViewById(R.id.iv_network_state);
        tvCallNumber = findViewById(R.id.tv_meeting_number);
        tvCallDuration = findViewById(R.id.tv_call_duration);
        ivChangeVolume = findViewById(R.id.iv_change_volume);
        ivChangeCamera = findViewById(R.id.iv_change_camera);
        ivHangup = findViewById(R.id.iv_hangup);

        videoPagerIndicator = findViewById(R.id.pager_indicator_video);
        videoPager = findViewById(R.id.video_pager);
        videoPagerAdapter = new VideoPagerAdapter(getSupportFragmentManager());
        videoPagerAdapter.setVideoCallback(this);
        videoPager.setAdapter(videoPagerAdapter);

        videoPagerIndicator.setViewPager(videoPager);
        myVideoPagerListener = new MyVideoPagerListener();
        videoPagerIndicator.setOnPageChangeListener(myVideoPagerListener);


        ivOnOffCamera = findViewById(R.id.iv_on_off_camera);
        tvOnOffCamera = findViewById(R.id.tv_on_off_camera);

        ivOnOffMic = findViewById(R.id.iv_on_off_mic);
        tvOnOffMic = findViewById(R.id.tv_on_off_mic);

        tvRoomMember = findViewById(R.id.tv_room_member);


        llHostMeeting = findViewById(R.id.ll_host_meeting);
        Toolbar hostMeetingToolbar = findViewById(R.id.hold_meeting_toolbar);
        hostMeetingToolbar.setNavigationOnClickListener(v -> onBackPressed());
        rvMeetingMember = findViewById(R.id.rv_meeting_member);
        rvMeetingMember.setLayoutManager(new LinearLayoutManager(this));
        memberAdapter = new MeetingMemberAdapter();
        memberAdapter.bindToRecyclerView(rvMeetingMember);

        llFinishShare = findViewById(R.id.ll_finish_share);
        shareScreenView = findViewById(R.id.share_screen);
        llLockPeople = findViewById(R.id.layout_lock_people);
        ivOffFullScreen = findViewById(R.id.iv_off_full_screen);


        ivHangup.setOnClickListener(this);
        ivChangeCamera.setOnClickListener(this);
        ivChangeVolume.setOnClickListener(this);

        llFinishShare.setOnClickListener(this);
        llLockPeople.setOnClickListener(this);

        findViewById(R.id.ll_on_off_mic).setOnClickListener(this);
        findViewById(R.id.ll_on_off_camera).setOnClickListener(this);
        findViewById(R.id.rl_room_member).setOnClickListener(this);
        findViewById(R.id.ll_more_item).setOnClickListener(this);

        findViewById(R.id.iv_off_full_screen).setOnClickListener(this);
    }

    private void initData() {
        // 注册白板监听(接收远端白板, 本地打开白板结果在此处回调)
        whiteboardChangeListener = new MyWhiteboardChangeListener();
        NemoSDK.getInstance().registerWhiteboardChangeListener(whiteboardChangeListener);

        // 横竖屏监听, 需要横竖屏切换打开此处 ,  enableScreenOrientation = false
        orientationEventListener = new MyOrientationEventListener(MeetingRoomActivity.this);
        orientationEventListener.enable();
        enableScreenOrientation = true;
        boolean isLandscape = getResources().getConfiguration().orientation == 2;
        videoPagerAdapter.setLandscape(isLandscape);

        NemoSDK.getInstance().setOnStateChangeListener(new NemoSDK.OnStateChangeListener() {
            @Override
            public void onConnectStateChanged(boolean connected) {
                logTag("连接状态: " + connected);
            }

            @Override
            public void unauthorized() {
                logTag("unauthorized 需要重新登录下");
            }

            @Override
            public void onTokenExpired() {
                logTag("onTokenExpired: 需要刷新Token");
                TokenInfo tokenInfo = XylinkStore.getInstant().getTokenInfo();
                if (tokenInfo == null) {
                    return;
                }
                NemoSDK.getInstance().refreshToken(
                        tokenInfo.getRefreshToken(), new RefreshTokenCallback() {
                    @Override
                    public void onRefreshTokenSuccess(TokenInfo tokenInfo) {
                        logTag("onRefreshSuccess: " + tokenInfo.toString());
                        //保存刷新 tokenInfo
                        XylinkStore.getInstant().setTokenInfo(tokenInfo);
                    }

                    @Override
                    public void onRefreshTokenFail(String errorCode, String msg) {
                        logTag("onRefreshFail: " + errorCode);
                    }
                });
            }
        });
        NemoSDK.getInstance().setNemoSDKListener(new SimpleNemoSDkListener() {
            @Override
            public void onCallStateChange(CallState state, String reason) {
                switch (state) {
                    case CONNECTING:
                        logTag("CONNECTING");
                        NemoSDK.getInstance().setLayoutBuilder(new SpeakerLayoutBuilder());
                        break;
                    case DISCONNECTED:
                        logTag("DISCONNECTED");
                        Observable.just(0).observeOn(AndroidSchedulers.mainThread())
                                .subscribe(integer -> showCallDisconnected(reason));
                        break;
                    case CONNECTED:
                        logTag("CONNECTED");
                        Observable.just(0).observeOn(AndroidSchedulers.mainThread())
                                .subscribe(integer -> showCallConnected());
                        break;
                    default:
                        break;
                }
            }

            @Override
            public void onVideoDataSourceChange(List<VideoInfo> videoInfos, boolean hasVideoContent) {
                Observable.just(videoInfos).observeOn(AndroidSchedulers.mainThread()).subscribe(new Consumer<List<VideoInfo>>() {
                    @Override
                    public void accept(List<VideoInfo> videoInfos) throws Exception {
                        showVideoDataSourceChange(videoInfos, hasVideoContent);
                    }
                }, throwable -> {

                });
            }

            @Override
            public void onRosterChange(RosterWrapper roster) {
                runMainThreadOb(integer -> {
                    onRosterChanged(roster.getParticipantsNum() + 1, roster);
                });
            }

            @Override
            public void onConfMgmtStateChanged(ConfMgmtState confMgmtState) {
                runMainThreadOb(integer -> {
                    showConfMgmtStateChanged(confMgmtState.operation,
                            confMgmtState.muteIsDisabled, confMgmtState.chairmanUri);
                });
            }



            @Override
            public void onKickOut(int code, int reason) {
                runMainThreadOb(integer -> {
                    showKickout(code, reason);
                });
            }

            @Override
            public void onNetworkIndicatorLevel(int level) {
                runMainThreadOb(integer -> {
                    showNetLevel(level);
                });
            }

            @Override
            public void onVideoStatusChange(int videoStatus) {
                runMainThreadOb(integer -> {
                    showVideoStatusChange(videoStatus);
                });
            }

            @Override
            public void onDualStreamStateChange(NemoDualState state, String reason, int type) {
                runMainThreadOb(integer -> {
                    logTag("共享回调:" + type + "-" + reason);
                    if (type == DUAL_TYPE_PICTURE) {
                        updateSharePictures(state);
                    } else if (type == DUAL_TYPE_CONTENT) {
                        updateShareScreen(state);
                    }
                });
            }
        });
    }

    private void hideOrShowToolbar(boolean show) {
        if (show) {
            hideToolbar();
        } else {
            showToolbar(sDefaultTimeout);
        }
    }

    private void hideToolbar() {
        meetingToolbar.setVisibility(GONE);
        isToolbarShowing = false;
    }

    private final Runnable mFadeOut = this::hideToolbar;

    private void showToolbar(int timeout) {
        if (!isToolbarShowing) { // show toolbar
            meetingToolbar.setVisibility(View.VISIBLE);
            isToolbarShowing = true;
        }
        if (timeout != 0) {
            handler.removeCallbacks(mFadeOut);
            handler.postDelayed(mFadeOut, timeout);
        }
    }

    // 通话时长
    private void initCallDuration() {
        if (compositeDisposable != null && !compositeDisposable.isDisposed()) {
            compositeDisposable.clear();
        }
        compositeDisposable.add(Observable.interval(1, TimeUnit.SECONDS)
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(aLong -> tvCallDuration.setText(CommonTime.formatTime(aLong))));
    }

    @Override
    public void onClick(View v) {
        int id = v.getId();
        if (id == R.id.iv_hangup) {
            NemoSDK.getInstance().hangup();
            NemoSDK.getInstance().releaseLayout();
            NemoSDK.getInstance().releaseCamera();
            finish();
        }
        else if (id == R.id.ll_on_off_mic) {
            if (isMuteBtnEnable) {
                updateMuteStatus(!NemoSDK.getInstance().isMicMuted());
            } else {
                // 举手/取消举手/结束发言
                switch (muteStatus) {
                    case MuteStatus.HAND_UP:
                        NemoSDK.getInstance().handUp();
                        muteStatus = MuteStatus.HAND_DOWN;
                        ivOnOffMic.setImageResource(R.drawable.icon_handdown);
                        tvOnOffMic.setText("取消举手");
                        break;
                    case MuteStatus.HAND_DOWN:
                        NemoSDK.getInstance().handDown();
                        muteStatus = MuteStatus.HAND_UP;
                        ivOnOffMic.setImageResource(R.drawable.icon_hand_up);
                        tvOnOffMic.setText("举手发言");
                        break;
                    case MuteStatus.END_SPEACH:
                        NemoSDK.getInstance().endSpeech();
                        muteStatus = MuteStatus.HAND_UP;
                        ivOnOffMic.setImageResource(R.drawable.icon_hand_up);
                        tvOnOffMic.setText("举手发言");
                        break;
                }
            }
        }
        else if (id == R.id.ll_on_off_camera) {
            isVideoMute = !isVideoMute;
            NemoSDK.getInstance().setVideoMute(isVideoMute);
            setVideoState(isVideoMute);
        }
        else if (id == R.id.rl_room_member) {
            showHostMeeting();
        }
        else if (id == R.id.ll_more_item) {
            boolean ableWhiteboard = (shareState != ShareState.WHITEBOARD) && (shareState != ShareState.SCREEN);
            boolean ableShareScreed = (shareState != ShareState.WHITEBOARD) && (shareState != ShareState.SCREEN);
            boolean ableScreenshot = !isVideoMute;
            new MoreItemDialog(this, ableWhiteboard, ableShareScreed, ableScreenshot,
                    new MoreItemDialog.OnItemClickListener() {
                @Override
                public void onItemClick(MoreItemDialog.MoreItemType itemType) {
                    dealClickMoreItem(itemType);
                }
            }).show();
        }
        else if (id == R.id.iv_change_camera) {
            NemoSDK.getInstance().switchCamera(defaultCameraFront ? 0 : 1);  // 0：后置 1：前置
            defaultCameraFront = !defaultCameraFront;
        }
        else if (id == R.id.iv_change_volume) {
            defaultVolumeSpeaker = !defaultVolumeSpeaker;
            NemoSDK.getInstance().switchSpeakerOnModle(defaultVolumeSpeaker);
            ivChangeVolume.setImageResource(defaultVolumeSpeaker ?
                    R.drawable.icon_volume_middle : R.drawable.icon_volume_modle);
        }
        else if (id == R.id.iv_off_full_screen) {
            for (VideoFragment videoFragment : videoPagerAdapter.getFragments()) {
                if (videoFragment instanceof GalleryVideoFragment) {
                    ((GalleryVideoFragment) videoFragment).unlockLayout();
                }
            }
        }
        else if (id == R.id.ll_finish_share) {
            if (this.shareState != null) {
                handleShareEvent(this.shareState);
            }
        }
        else if (id == R.id.layout_lock_people) {
            ((SpeakerVideoFragment) videoPagerAdapter.getItem(0)).unlockLayout();
            llLockPeople.setVisibility(GONE);
        }
    }

    private void showCallConnected() {
        isCallStart = true;
        initCallDuration();

        Intent dataIntent = getIntent();
        if (dataIntent.hasExtra(MeetingConstant.CALL_NUMBER)) {
            callNumber = dataIntent.getStringExtra(MeetingConstant.CALL_NUMBER);

        }
        if (dataIntent.hasExtra(MeetingConstant.MEETING_ROOM_NAME)) {
            String roomName = dataIntent.getStringExtra(MeetingConstant.MEETING_ROOM_NAME);
            tvCallNumber.setText(roomName);
            if (roomName.length() >= 10) {
                tvCallNumber.setTextSize(TypedValue.COMPLEX_UNIT_SP, 10);
            }
        } else {
            tvCallNumber.setText(callNumber);
        }

        showToolbar(sDefaultTimeout);
        videoPagerAdapter.setLocalVideoInfo(buildLocalLayoutInfo());

        if (dataIntent.getBooleanExtra(MUTE_VIDEO, false)) {
            isVideoMute = true;
            NemoSDK.getInstance().setVideoMute(isVideoMute);
            setVideoState(isVideoMute);
        }
        if (dataIntent.getBooleanExtra(MUTE_AUDIO, false) && isMuteBtnEnable) {
            updateMuteStatus(true);
        }
        // 会控消息
        if (confMgmtStateObserver != null) {
            confMgmtStateObserver.subscribe(
                    aBoolean -> videoPagerAdapter.setLocalMicMute(aBoolean),
                    throwable -> {
                    });
        }
        int orientation = getResources().getConfiguration().orientation;
        if (orientation == Configuration.ORIENTATION_LANDSCAPE && isTabletDevice(this)) {
            NemoSDK.getInstance().setOrientation(Orientation.LANDSCAPE);
        }
    }

    private VideoInfo buildLocalLayoutInfo() {
        VideoInfo li = new VideoInfo();
        li.setLayoutVideoState(Enums.LAYOUT_STATE_RECEIVED);
        li.setDataSourceID(NemoSDK.getLocalVideoStreamID());
        li.setVideoMuteReason(Enums.MUTE_BY_USER);
        li.setRemoteName(NemoSDK.getInstance().getUserName());
        li.setParticipantId((int) NemoSDK.getInstance().getUserId());
        li.setRemoteID(RemoteUri.generateUri(String.valueOf(NemoSDK.getInstance().getUserId()), Enums.DEVICE_TYPE_SOFT));
        localVideoInfo = li;
        return li;
    }

    /**
     * 判断是否平板设备
     * @param context
     * @return true:平板,false:手机
     */
    private boolean isTabletDevice(Context context) {
        return (context.getResources().getConfiguration().screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) >=
                Configuration.SCREENLAYOUT_SIZE_LARGE;
    }

    public void showCallDisconnected(String reason) {
        if ("SIG:420".equals(reason)) {
            showToast("无效会议号");
        } else if ("SIG:422".equals(reason)) {
            showToast("您已被主持人请出会议");
        } else if ("SIG:424".equals(reason)) {
            showToast("主持人已结束会议");
        }
        finish();
    }

    private void showVideoDataSourceChange(List<VideoInfo> videoInfos, boolean hasVideoContent) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            // case-fix: 如果正在共享屏幕, 收到content则APP回到前台
            if (hasVideoContent && !ActivityUtils.isAppForeground(this)
                    && !(screenPresenter != null && screenPresenter.isSharingScreen())) {
                ActivityUtils.moveTaskToFront(this);
            }
        }
        mRemoteVideoInfos = videoInfos;
        if ((currentPagerIndex == 0 || currentPagerIndex == 1) && !hasVideoContent) {
            firstPagerVideoInfo = videoInfos;
        }
        logTag("setRemoteVideoInfo:" + mRemoteVideoInfos.size());
        VideoFragment fragment = (VideoFragment) videoPagerAdapter.getItem(currentPagerIndex);
        fragment.setRemoteVideoInfo(videoInfos, hasVideoContent);

        setLayoutBuilder();
    }

    private void onRosterChanged(int totalNumber, RosterWrapper rosters) {
        this.rosterWrapper = rosters;
        videoPagerAdapter.setTotalMeetingMember(totalNumber);
        videoPagerIndicator.notifyDataSetChanged();
        if (totalNumber > 1) {
            tvRoomMember.setText(Integer.toString(totalNumber));
        } else {
            tvRoomMember.setText("");
        }

        List<MeetingMemberAdapter.MeetingMemberBean> member = new ArrayList<>();
        member.add(new MeetingMemberAdapter.MeetingMemberBean(
                localVideoInfo.getParticipantId(),
                localVideoInfo.getRemoteName(),
                localVideoInfo.isAudioMute(),
                localVideoInfo.isVideoMute()));
        for (Roster roster : rosters.getRosters()) {
            member.add(new MeetingMemberAdapter.MeetingMemberBean(
                    roster.getParticipantId(),
                    roster.getDeviceName(),
                    roster.isAudioMute(),
                    roster.isVideoMute()));
        }
        memberAdapter.setNewData(member);
    }


    //视频关闭或者开启
    private void setVideoState(boolean videoMute) {
        videoPagerAdapter.setLocalVideoMute(videoMute);
        if (videoMute) {
            ivOnOffCamera.setImageResource(R.drawable.icon_camera_off);
            tvOnOffCamera.setText(getResources().getString(R.string.video_camera_on));
            ivChangeCamera.setVisibility(View.GONE);
        } else {
            ivOnOffCamera.setImageResource(R.drawable.icon_camera_on);
            tvOnOffCamera.setText(getResources().getString(R.string.video_camera_off));
            ivChangeCamera.setVisibility(View.VISIBLE);
        }
    }

    private void updateMuteStatus(boolean isMute) {
        NemoSDK.getInstance().enableMic(isMute, true);
        videoPagerAdapter.setLocalMicMute(isMute);
        if (isMute) {
            ivOnOffMic.setImageResource(R.drawable.icon_mic_off);
            tvOnOffMic.setText(getResources().getString(R.string.video_mic_on));
        } else {
            ivOnOffMic.setImageResource(R.drawable.icon_mic_on);
            tvOnOffMic.setText(getResources().getString(R.string.video_mic_off));
        }
    }

    public void showKickout(int code, int reason) {
        showToast("会议被踢出: " + reason);
        logTag("会议被踢出: " + reason);
        finish();
    }


    /**
     * 处理会控消息
     * 控制操作：静音、非静音
     * 控制状态：举手发言、取消举手、结束发言
     *
     * @param operation        操作：mute/unmute
     * @param isMuteIsDisabled 是否为强制静音 true强制静音
     */
    private void showConfMgmtStateChanged(String operation, boolean isMuteIsDisabled, String chairmanUri) {
        isMuteBtnEnable = !isMuteIsDisabled;
        if ("mute".equalsIgnoreCase(operation)) {
            NemoSDK.getInstance().enableMic(true, isMuteIsDisabled);
            if (isMuteIsDisabled) {
                // 强制静音
                showToast("主持人强制静音, 请举手发言");
                muteStatus = MuteStatus.HAND_UP;
                ivOnOffMic.setImageResource(R.drawable.icon_hand_up);
                tvOnOffMic.setText("举手发言");
            } else {
                showToast("您已被静音");
                ivOnOffMic.setImageResource(R.drawable.icon_mic_off);
                tvOnOffMic.setText("取消静音");
            }
            if (isCallStart) {
                videoPagerAdapter.setLocalMicMute(true);
            }
            confMgmtStateObserver = Observable.just(true);
        } else if ("unmute".equalsIgnoreCase(operation)) {
            NemoSDK.getInstance().enableMic(false, false);
            if (isMuteIsDisabled) {
                muteStatus = MuteStatus.END_SPEACH;
                ivOnOffMic.setImageResource(R.drawable.icon_end_speech);
                tvOnOffMic.setText("结束发言");
            } else {
                ivOnOffMic.setImageResource(R.drawable.icon_mic_on);
                tvOnOffMic.setText("静音");
            }
            if (isCallStart) {
                videoPagerAdapter.setLocalMicMute(false);
            }
            confMgmtStateObserver = Observable.just(false);
        }
    }


    private void showNetLevel(int level) {
        if (ivNetworkState == null) {
            return;
        }
        switch (level) {
            case 4:
                ivNetworkState.setImageResource(R.drawable.network_state_four);
                break;
            case 3:
                ivNetworkState.setImageResource(R.drawable.network_state_three);
                break;
            case 2:
                ivNetworkState.setImageResource(R.drawable.network_state_two);
                break;
            case 1:
                ivNetworkState.setImageResource(R.drawable.network_state_one);
                break;
        }
    }

    private void showVideoStatusChange(int videoStatus) {
        if (videoStatus == VideoStatus.VIDEO_STATUS_NORMAL) {
            showToast("网络正常");
        } else if (videoStatus == VideoStatus.VIDEO_STATUS_LOW_AS_LOCAL_BW) {
            showToast("本地网络不稳定");
        } else if (videoStatus == VideoStatus.VIDEO_STATUS_LOW_AS_LOCAL_HARDWARE) {
            showToast("系统忙，视频质量降低");
        } else if (videoStatus == VideoStatus.VIDEO_STATUS_LOW_AS_REMOTE) {
            showToast("对方网络不稳定");
        } else if (videoStatus == VideoStatus.VIDEO_STATUS_NETWORK_ERROR) {
            showToast("网络不稳定，请稍候");
        } else if (videoStatus == VideoStatus.VIDEO_STATUS_LOCAL_WIFI_ISSUE) {
            showToast("WiFi信号不稳定");
        }
    }


    //会控
    private void showHostMeeting() {
        llHostMeeting.setVisibility(VISIBLE);

    }

    private void hideHostMeeting() {
        if (llHostMeeting != null && llHostMeeting.getVisibility() == View.VISIBLE) {
            llHostMeeting.setVisibility(View.GONE);

        }
    }


    private void showToast(String msg) {
        Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
    }


    private void dealClickMoreItem(MoreItemDialog.MoreItemType itemType) {
        if (itemType == MoreItemDialog.MoreItemType.Whiteboard) {
            //白板
            handleShareEvent(shareState);
        } else if (itemType == MoreItemDialog.MoreItemType.Screenshot) {
            //截图
            byte[] bitmapData = NemoSDK.getInstance().getLocalVideoData(); //获取图像数据
            Bitmap bitmap = BitmapFactory.decodeByteArray(bitmapData, 0, bitmapData.length); //转换成Bitmap

            //保存Bitmap到文件
            String fileName = "MeetingScreenshot-" + System.currentTimeMillis() + ".jpg";
            File targetFile = new File(XylinkStore.getScreenShotDir(), fileName);
            logTag(targetFile.getAbsolutePath());
            if (targetFile.exists()) {
                targetFile.delete();
            }
            try {
                FileOutputStream out = new FileOutputStream(targetFile);
                bitmap.compress(Bitmap.CompressFormat.JPEG, 100, out);
                out.flush();
                out.close();
                showToast("保存成功");
            } catch (Exception e) {
                showToast("保存失败");
                e.printStackTrace();
                logTag(e.toString());
            }
        } else if (itemType == MoreItemDialog.MoreItemType.ShareScreen) {
            //屏幕共享
//            NemoSDK.getInstance().dualStreamStart(ContentType.CONTENT_TYPE_SCREEN, false);
            if (screenPresenter == null) {
                screenPresenter = new ScreenPresenter(MeetingRoomActivity.this);
            }
            screenPresenter.startShare();
        }
    }

    private void handleShareEvent(ShareState shareState) {
        switch (shareState) {
            case NONE:
                NemoSDK.getInstance().startWhiteboard();
                break;
            case WHITEBOARD:
                new CustomAlertDialog(this).builder()
                        .setTitle(getString(R.string.exit_white_board_title))
                        .setMsg(getString(R.string.exit_white_board_content))
                        .setPositiveButton(getString(R.string.sure), v1 -> {
                            NemoSDK.getInstance().stopWhiteboard();
                        })
                        .setNegativeButton(getString(R.string.cancel), v12 -> {
                        }).setCancelable(false).show();
                break;
            case SCREEN:
                if (screenPresenter != null && screenPresenter.isSharingScreen()) {
                    NemoSDK.getInstance().dualStreamStop(ContentType.CONTENT_TYPE_SCREEN_WITH_AUDIO);
                }
                break;
            default:
        }
    }

    @Override
    public boolean onVideoCellSingleTapConfirmed(VideoCell cell) {
        hideOrShowToolbar(isToolbarShowing);
        return false;
    }

    @Override
    public boolean onVideoCellDoubleTap(VideoCell cell) {
        return false;
    }

    @Override
    public void onLockLayoutChanged(int pid) {
        if (currentPagerIndex == 0) {
            llLockPeople.setVisibility(VISIBLE);
        } else {
            llLockPeople.setVisibility(GONE);
        }
    }

    @Override
    public void onFullScreenChanged(VideoCell cell) {
        if (cell != null) {
            fullVideoInfo = cell.getLayoutInfo();
            if (currentPagerIndex != 0) {
                ivOffFullScreen.setVisibility(VISIBLE);
            } else {
                ivOffFullScreen.setVisibility(GONE);
            }
        } else {
            fullVideoInfo = null;
            ivOffFullScreen.setVisibility(GONE);
        }
    }

    @Override
    public void onVideoCellGroupClicked(View group) {
        hideOrShowToolbar(isToolbarShowing);
    }

    @Override
    public void onWhiteboardClicked() {
        hideOrShowToolbar(isToolbarShowing);
    }

    //=========================================================================================
    // share screen demo: 分享屏幕跟分享白板同时只允许一方
    //=========================================================================================
    @RequiresApi(api = Build.VERSION_CODES.M)
    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent intent) {
        super.onActivityResult(requestCode, resultCode, intent);
        if (SharingValues.REQUEST_SHARE_SCREEN == requestCode) {
            if (resultCode == RESULT_OK) {
                if (screenPresenter != null) {
                    screenPresenter.onResult(requestCode, resultCode, intent);
                }
            } else {
                showToast("屏幕分享失败");
            }
        } else if (SharingValues.REQUEST_FLOAT_PERMISSION == requestCode) {
            // home screen float view
            if (Settings.canDrawOverlays(MeetingRoomActivity.this)) {
                if (screenPresenter != null) {
                    screenPresenter.gotPermissionStartShare();
                }
            } else {
                showToast("需要打开悬浮窗权限");
            }
        }
    }

    public void updateSharePictures(NemoSDKListener.NemoDualState state) {
        if (state == NemoSDKListener.NemoDualState.NEMO_DUAL_STAT_IDLE) {
//            pictureHandler.removeMessages(MSG_SHARE_PICTURE);
//            pictureData = null;
//            pagerPicture.setVisibility(GONE);
//            pageIndicator.setVisibility(GONE);
//            isSharePicture = false;
            resetShareStates(false, ShareState.IMAGE);
        } else if (state == NemoSDKListener.NemoDualState.NEMO_DUAL_STATE_RECEIVING) {
            resetShareStates(true, ShareState.IMAGE);
//            picturePagerAdapter = new PicturePagerAdapter(getSupportFragmentManager());
//            picturePagerAdapter.setOnPagerListener(() -> hideOrShowToolbar(isToolbarShowing));
//            pagerPicture.setAdapter(picturePagerAdapter);
//            pageIndicator.setViewPager(pagerPicture);
//            pageIndicator.setOnPageChangeListener(new MyPagerListener());
//            picturePagerAdapter.setPicturePaths(picturePaths);
//            picturePagerAdapter.notifyDataSetChanged();
//            pageIndicator.setVisibility(VISIBLE);
//            pagerPicture.setVisibility(VISIBLE);
        } else if (state == NemoSDKListener.NemoDualState.NEMO_DUAL_STATE_NOBANDWIDTH) {
            Toast.makeText(this, "带宽不足, 网络不稳定, 无法分享", Toast.LENGTH_SHORT).show();
        } else {
            Toast.makeText(this, "错误, 无法分享", Toast.LENGTH_SHORT).show();
        }
    }



    public void updateShareScreen(NemoSDKListener.NemoDualState state) {
        if (state == NemoSDKListener.NemoDualState.NEMO_DUAL_STAT_IDLE) {
            if (screenPresenter != null && screenPresenter.isSharingScreen()) {
                logTag("updateShareScreen stop");
                screenPresenter.stopShare();
            }
            shareScreenView.setVisibility(GONE);
            resetShareStates(false, ShareState.SCREEN);
        } else if (state == NemoSDKListener.NemoDualState.NEMO_DUAL_STATE_RECEIVING) {
            resetShareStates(true, ShareState.SCREEN);
            // show floating view
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                ActivityUtils.goHome(this);
                if (screenPresenter != null) {
                    screenPresenter.showFloatView(); // 显示悬浮窗
                }
                shareScreenView.setVisibility(VISIBLE);
            }
        } else {
            showToast("正在分享, 请稍后");
        }
    }


    @SuppressLint("CheckResult")
    private void runMainThreadOb(Consumer<Integer> consumer) {
        Observable.just(0)
                .compose(bindToLifecycle())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(consumer, throwable -> {});
    }


    private class MyWhiteboardChangeListener implements WhiteboardChangeListener {

        @Override
        public void onWhiteboardStart() {
            runMainThreadOb(integer -> {

                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                    if (!ActivityUtils.isAppForeground(MeetingRoomActivity.this)
                            && !(screenPresenter != null && screenPresenter.isSharingScreen())) {
                        ActivityUtils.moveTaskToFront(MeetingRoomActivity.this);
                    }
                }

                videoPager.setScanScroll(false);
                myVideoPagerListener.onPageSelected(0);
                videoPager.setCurrentItem(0, false);
                videoPagerAdapter.setLandscape(true);
                NemoSDK.getInstance().setOrientation(Orientation.LANDSCAPE);
                videoPagerAdapter.onWhiteboardStart();
                resetShareStates(true, ShareState.WHITEBOARD);
            });
        }

        @Override
        public void onWhiteboardStop() {
            runMainThreadOb(integer -> {
                videoPager.setScanScroll(true);
                videoPagerAdapter.onWhiteboardStop();
                resetShareStates(false, ShareState.WHITEBOARD);
            });
        }

        /**
         * 处理白板数据
         *
         * @param message 白板数据
         */
        @Override
        public void onWhiteboardMessage(String message) {
            runMainThreadOb(integer -> {
                videoPagerAdapter.onWhiteboardMessage(message);
            });
        }

        @Override
        public void onWhiteboardMessages(ArrayList<String> messages) {
            runMainThreadOb(integer -> {
                if (getRequestedOrientation() == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
                        || getRequestedOrientation() == ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT) {
                    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
                    NemoSDK.getInstance().setOrientation(Orientation.LANDSCAPE);
                }
                videoPagerAdapter.onWhiteboardMessages(messages);
            });
        }

        @Override
        public void onAnnotationSending() {

        }
    }

    private void resetShareStates(boolean isSharing, ShareState shareState) {
        if (isSharing) {
            this.shareState = shareState;
            llFinishShare.setVisibility(VISIBLE);
        } else {
            this.shareState = ShareState.NONE;
            llFinishShare.setVisibility(GONE);
        }
    }


    private static final int MSG_ORIENTATION_CHANGED = 60001;
    private Handler orientationHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            if (msg.what == MSG_ORIENTATION_CHANGED) {
                handleOrientationChanged(msg.arg1);
            }
        }
    };

    private void handleOrientationChanged(int rotation) {
        int screenOrientation = getResources().getConfiguration().orientation;
        if (((rotation >= 0) && (rotation < 45)) || (rotation > 315)) {
            // 竖屏 0度：手机默认竖屏状态（home键在正下方）
            // NOTE: 白板状态默认支持横屏 竖屏会拉伸变形,  画廊模式也默认横屏(口, 品, 田)
            if (screenOrientation != ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
                    && screenOrientation != ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT) {
                if (!SpeakerVideoGroup.isShowingWhiteboard()) {
                    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
                    videoPagerAdapter.setLandscape(false);
                    NemoSDK.getInstance().setOrientation(Orientation.PORTRAIT);
                }
            }
        } else if (rotation > 225 && rotation < 315) {
            // 横屏 270度：手机顺时针旋转270度横屏，（home键在右侧）
            if (screenOrientation != ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) {
                setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
                videoPagerAdapter.setLandscape(true);
                NemoSDK.getInstance().setOrientation(Orientation.LANDSCAPE);
            }
        } else if (rotation > 45 && rotation < 135) {
            // 反向横屏 90度：手机顺时针旋转90度横屏（home建在左侧）
            if (screenOrientation != ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE) {
                setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE);
                videoPagerAdapter.setLandscape(true);
                NemoSDK.getInstance().setOrientation(Orientation.REVERSE_LANDSCAPE);
            }
        } else if (rotation > 135 && rotation < 225) {

        }
    }


    private class MyOrientationEventListener extends OrientationEventListener {

        public MyOrientationEventListener(Context context) {
            super(context);
        }

        @Override
        public void onOrientationChanged(int orientation) {
            if (enableScreenOrientation) {
                orientationHandler.removeMessages(MSG_ORIENTATION_CHANGED);
                Message msg = handler.obtainMessage(MSG_ORIENTATION_CHANGED, orientation, 0);
                orientationHandler.sendMessageDelayed(msg, 100);
            }
        }
    }

    public class MyVideoPagerListener extends ViewPager.SimpleOnPageChangeListener {
        boolean first = true;

        @Override
        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
            super.onPageScrolled(position, positionOffset, positionOffsetPixels);
            if (first && positionOffset == 0 && positionOffsetPixels == 0) {
                onPageSelected(0);
                first = false;
            }
        }

        @Override
        public void onPageSelected(int position) {
            super.onPageSelected(position);
            if (videoPagerAdapter.getCurrentIndex() != position) {
                videoPagerAdapter.setCurrentIndex(position);
            }
            currentPagerIndex = position;
            // 0 / 1 复用List<VideoInfo>
            VideoFragment videoFragment = videoPagerAdapter.getItem(position);
            if (position == 0) {
                videoFragment.setRemoteVideoInfo(firstPagerVideoInfo, false);
                NemoSDK.getInstance().setLayoutBuilder(new SpeakerLayoutBuilder());
            } else if (position == 1) {
                videoFragment.setRemoteVideoInfo(firstPagerVideoInfo, false);
                NemoSDK.getInstance().setLayoutBuilder(new GalleryLayoutBuilder(1));
            } else {
                // other pager
                NemoSDK.getInstance().setLayoutBuilder(new GalleryLayoutBuilder(position));
            }
            videoFragment.startRender();
        }
    }

    private void setLayoutBuilder() {
        if (currentPagerIndex == 0) {
            NemoSDK.getInstance().setLayoutBuilder(new SpeakerLayoutBuilder());
        } else if (currentPagerIndex == 1) {
            NemoSDK.getInstance().setLayoutBuilder(new GalleryLayoutBuilder(1));
        } else {
            // other pager
            NemoSDK.getInstance().setLayoutBuilder(new GalleryLayoutBuilder(currentPagerIndex));
        }
    }

    private void logTag(String msg) {
        Log.e("MeetingRoom", msg);
    }

    public static Intent getStartIntent(Context context, String callNumber) {
        return getStartIntent(context, callNumber, null, false, true);
    }

    public static Intent getStartIntent(Context context, String callNumber, String meetingRoomName) {
        return getStartIntent(context, callNumber, meetingRoomName, false, true);
    }

    public static Intent getStartIntent(
            Context context, String callNumber, String meetingRoomName, boolean muteVideo, boolean muteAudio) {
        Intent intent = new Intent(context, MeetingRoomActivity.class);
        intent.putExtra(MeetingConstant.CALL_NUMBER, callNumber);
        if (meetingRoomName != null && !TextUtils.isEmpty(meetingRoomName)) {
            intent.putExtra(MeetingConstant.MEETING_ROOM_NAME, meetingRoomName);
        }

        intent.putExtra(MUTE_VIDEO, muteVideo);
        intent.putExtra(MUTE_AUDIO, muteAudio);
        return intent;
    }


}